﻿
// **************************************************************************
//    Class Created by Mick Doherty (Dotnetrix) June 2010
//    http://dotnetrix.co.uk/
//
//    ...for GlassUI (an AeroGlass UI Library)
//    http://glassui.codeplex.com/
// **************************************************************************

using System;
using System.ComponentModel;
using System.Drawing;
using System.Security.Permissions;
using System.Windows.Forms;
using GlassUI.Design;

namespace GlassUI
{
    [Designer(typeof(StackedPanelControlDesigner))]
    [DefaultProperty("StackedPanels")]
    [Docking(DockingBehavior.Ask)]
    public class StackedPanelControl : GlassControlBase
    {
        public StackedPanelControl()
            : base()
        {
            this.SetStyle(ControlStyles.Selectable, true);
        }

        public event EventHandler<SelectedIndexChangeEventArgs> SelectedIndexChange;

        #region Private Instance variables

        private BorderStyle borderStyle = BorderStyle.FixedSingle;
        private int selectedIndex = -1;
        private ImageList imageList;

        private Size displaySize = Size.Empty;

        #endregion

        #region Properties

        protected override CreateParams CreateParams
        {
            [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)]
            [SecurityPermission(SecurityAction.InheritanceDemand, Flags = SecurityPermissionFlag.UnmanagedCode)]
            get
            {
                const int WS_BORDER = 0x800000;
                const int WS_EX_CLIENTEDGE = 0x200;

                CreateParams cp = base.CreateParams;

                switch (this.BorderStyle)
                {
                    case BorderStyle.Fixed3D:
                        cp.ExStyle |= WS_EX_CLIENTEDGE;
                        break;

                    case BorderStyle.FixedSingle:
                        cp.Style |= WS_BORDER;
                        break;

                    default:
                        break;
                }
                return cp;
            }
        }

        protected override System.Drawing.Size DefaultSize
        {
            get
            {
                return new Size(200,100);
            }
        }

        [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
        public override int CornerRadius
        {
            get
            {
                return base.CornerRadius;
            }
            set
            {
                base.CornerRadius = value;
            }
        }

        [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
        public override Corners RoundedCorners
        {
            get
            {
                return base.RoundedCorners;
            }
            set
            {
                base.RoundedCorners = value;
            }
        }

        [Category("Appearance")]
        [DefaultValue(typeof(BorderStyle), "FixedSingle")]
        [GlassUIDescription("BorderStyleDescription")]
        public BorderStyle BorderStyle
        {
            get { return borderStyle; }
            set
            {
                if (borderStyle != value)
                {
                    borderStyle = value;
                    this.UpdateStyles();
                }
            }
        }

        [Category("Appearance")]
        [DefaultValue(typeof(ImageList), null)]
        [GlassUIDescription("ButtonImageListDescr", "System.Windows.Forms")]
        public ImageList ImageList
        {
            get { return imageList; }
            set
            {
                imageList = value;
                foreach (StackedPanel panel in this.StackedPanels)
                    panel.Image = null;
                this.Invalidate();
            }
        }

        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        [GlassUIDescription("StackedPanelsDescription")]
        [MergableProperty(false)]
        public StackedPanelCollection StackedPanels
        {
            get { return this.Controls as StackedPanelCollection; }
        }

        [Browsable(false)]
        public int SelectedIndex
        {
            get { return selectedIndex; }
            set
            {
                if (selectedIndex != value)
                {
                    if (value >= 0 && value < this.Controls.Count)
                    {
                        int oldValue = selectedIndex;
                        SelectedIndexChangeEventArgs ev = new SelectedIndexChangeEventArgs(oldValue, value);
                        OnSelectedIndexChanged(ev);
                        if (!ev.Cancel)
                            selectedIndex = value;
                    }
                }
            }
        }

        #endregion

        #region Internal Methods

        internal void CalculateLayout()
        {
            int combinedHeaderHeight = 0;

            for (int id = 0; id < this.StackedPanels.Count; id++)
            {
                StackedPanel panel = StackedPanels[id];
                combinedHeaderHeight += panel.CollapsedHeight;
            }

            displaySize = new Size(this.ClientSize.Width, this.ClientSize.Height-combinedHeaderHeight);

            int offset = 0;

            for (int id = 0; id < this.StackedPanels.Count; id++)
            {
                StackedPanel panel = StackedPanels[id];
                int panelHeight = panel.CollapsedHeight;
                if (id == this.selectedIndex)
                    panelHeight += Math.Max(0, displaySize.Height);
                panel.Height = panelHeight;
                panel.Width = displaySize.Width;
                panel.Location = new Point(0, offset);
                offset += panel.Height;
            }
        }

        #endregion

        #region Overridden / Virtual Methods

        protected override Control.ControlCollection CreateControlsInstance()
        {
            return new StackedPanelCollection(this);
        }

        protected override bool IsInputKey(Keys keyData)
        {
            switch (keyData)
            {
                case Keys.Up:
                case Keys.Down:
                case Keys.Left:
                case Keys.Right:
                    return true;
                default:
                    return base.IsInputKey(keyData);
            }
        }
        
        protected override void OnChangeUICues(UICuesEventArgs e)
        {
            base.OnChangeUICues(e);
            if (e.ChangeFocus)
                if (this.selectedIndex != -1)
                    this.StackedPanels[this.selectedIndex].InvokeNCPaint();
        }

        protected override void OnControlAdded(ControlEventArgs e)
        {
            StackedPanel page = e.Control as StackedPanel;

            if (page == null && e.Control != null)
                throw new ArgumentException("Only VerticalTabPages may be added to a VerticalTabControl.", e.Control.Name);

            base.OnControlAdded(e);
            this.CalculateLayout();
        }

        protected override void OnControlRemoved(ControlEventArgs e)
        {
            base.OnControlRemoved(e);
            StackedPanel page = e.Control as StackedPanel;
            if (page != null)
            {
                int newIndex = Math.Max(this.SelectedIndex - 1, -1);
                if (newIndex == -1 && this.Controls.Count > 0)
                    newIndex = 0;
                this.selectedIndex = newIndex;
                this.CalculateLayout();
            }
        }

        protected override void OnGotFocus(EventArgs e)
        {
            base.OnGotFocus(e);
            if (this.selectedIndex != -1)
                this.StackedPanels[this.selectedIndex].InvokeNCPaint();
        }

        protected override void OnLostFocus(EventArgs e)
        {
            base.OnLostFocus(e);
            if (this.selectedIndex != -1)
                this.StackedPanels[this.selectedIndex].InvokeNCPaint();
        }

        protected override void OnResize(EventArgs e)
        {
            base.OnResize(e);
            CalculateLayout();
        }

        protected virtual void OnSelectedIndexChanged(SelectedIndexChangeEventArgs e)
        {
            if (this.SelectedIndexChange != null)
                this.SelectedIndexChange(this, e);
            if (!e.Cancel)
            {
                this.selectedIndex = e.Next;
                this.CalculateLayout();
                this.Invalidate(true);
            }

        }

        [PermissionSet(SecurityAction.Demand, Name = "FullTrust")]
        protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
        {
            bool result = base.ProcessCmdKey(ref msg, keyData);

            if (!result && this.Focused)
            {
                switch (keyData)
                {
                    case Keys.Down:
                    case Keys.Right: 
                    case Keys.Control | Keys.Tab:
                    case Keys.Control | Keys.PageDown:
                        this.SelectedIndex++;
                        return true;

                    case Keys.Up:
                    case Keys.Left:
                    case Keys.Control | Keys.Tab | Keys.Shift:
                    case Keys.Control | Keys.PageUp:
                        this.SelectedIndex--;
                        return true;

                    case Keys.Home:
                        this.SelectedIndex = 0;
                        return true;

                    case Keys.End:
                        this.SelectedIndex = this.Controls.Count - 1;
                        return true;
                        
                    default:
                        break;
                }
            }

            return result;
        }

        #endregion
    }

    public class SelectedIndexChangeEventArgs : CancelEventArgs
    {
        int current = -1;
        int next = -1;

        public int Current
        {
            get { return current; }
        }

        public int Next
        {
            get { return next; }
        }

        public SelectedIndexChangeEventArgs(int currentSelection, int nextSelection) : base()
        {
            current = currentSelection;
            next = nextSelection;
        }

    }

}
